package edu.cmu.cs.stage3.alice.authoringtool.util;

import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Point;
import java.awt.Toolkit;
import java.awt.event.FocusEvent;
import java.awt.event.KeyEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.Vector;

import javax.accessibility.Accessible;
import javax.accessibility.AccessibleContext;
import javax.accessibility.AccessibleRole;
import javax.accessibility.AccessibleSelection;
import javax.accessibility.AccessibleState;
import javax.swing.Action;
import javax.swing.ButtonModel;
import javax.swing.Icon;
import javax.swing.JComponent;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.KeyStroke;
import javax.swing.MenuElement;
import javax.swing.MenuSelectionManager;
import javax.swing.SwingConstants;
import javax.swing.UIDefaults;
import javax.swing.UIManager;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.EventListenerList;
import javax.swing.event.MenuEvent;
import javax.swing.event.MenuListener;
import javax.swing.event.PopupMenuEvent;
import javax.swing.event.PopupMenuListener;
import javax.swing.plaf.MenuItemUI;
import javax.swing.plaf.PopupMenuUI;

public class AliceMenu extends javax.swing.JMenu implements Accessible, MenuElement {

 //C @see #getUIClassID
 //C @see #readObject
 private static final String uiClassID = "MenuUI";

 //C The popup menu portion of the menu.
 private AlicePopupMenu popupMenu;

 //C The button's model listeners.  Default is <code>null</code>.
 private ChangeListener menuChangeListener = null;

 //C Only one <code>MenuEvent</code> is needed for each menu since the
 //C event's only state is the source property.  The source of events
 //C generated is always "this".  Default is <code>null</code>.
 private MenuEvent menuEvent = null;

 //C Used by the look and feel (L&F) code to handle
 //C implementation specific menu behaviors.
 private int delay;

 //C Set to true when a KEY_PRESSED event is received and the menu is
 //C selected, and false when a KEY_RELEASED (or focus lost) is received.
 //C If processKeyEvent is invoked with a KEY_TYPED or KEY_RELEASED event,
 //C and this is false, a MenuKeyEvent is NOT created. This is needed to
 //C avoid activating a menuitem when the menu and menuitem share the
 //C same mnemonic
 private boolean receivedKeyPressed;

 //C Constructs a new <code>AliceMenu</code> with no text.
 public AliceMenu() {
  this( "" );
 }
 //C Constructs a new <code>AliceMenu</code> with the supplied string
 //C as its text.
 //C @param s  the text for the menu label
 public AliceMenu( String s ) {
  super( s );
 }
 
 //Rem @Override
 public void updateUI() {
  setUI( (MenuItemUI)UIManager.getUI( this ) );

  if( popupMenu != null ) {
   popupMenu.setUI( (PopupMenuUI)UIManager.getUI( popupMenu ) );
  }
 }
 //C Returns the name of the L&F class that renders this component.
 //C @return the string "MenuUI"
 //C @see JComponent#getUIClassID
 //C @see UIDefaults#getUI
 //Rem @Override
 public String getUIClassID() {
  return uiClassID;
 }
 //    public void repaint(long tm, int x, int y, int width, int height) {
 //        Thread.currentThread().dumpStack();
 //        super.repaint(tm,x,y,width,height);
 //    }
 //C Sets the data model for the "menu button" -- the label
 //C that the user clicks to open or close the menu.
 //C @param newModel the <code>ButtonModel</code>
 //C @see #getModel
 //C @beaninfo
 //C description: The menu's model
 //C       bound: true
 //C      expert: true
 //C      hidden: true
 //Rem @Override
 public void setModel( ButtonModel newModel ) {
  ButtonModel oldModel = getModel();
  super.setModel( newModel );

  if( ( oldModel != null ) && ( menuChangeListener != null ) ) {
   oldModel.removeChangeListener( menuChangeListener );
   menuChangeListener = null;
  }

  model = newModel;

  if( newModel != null ) {
   menuChangeListener = createMenuChangeListener();
   newModel.addChangeListener( menuChangeListener );
  }
 }
 //C Returns true if the menu is currently selected (highlighted).
 //C @return true if the menu is selected, else false
 //Rem @Override
 public boolean isSelected() {
  return getModel().isSelected();
 }
 //C Sets the selection status of the menu.
 //C @param b  true to select (highlight) the menu; false to de-select
 //C        the menu
 //C @beaninfo
 //C      description: When the menu is selected, its popup child is shown.
 //C           expert: true
 //C           hidden: true
 //Rem @Override
 public void setSelected( boolean b ) {
  ButtonModel model = getModel();
  boolean oldValue = model.isSelected();

  if( ( accessibleContext != null ) && ( oldValue != b ) ) {
   if( b ) {
    accessibleContext.firePropertyChange( AccessibleContext.ACCESSIBLE_STATE_PROPERTY, null, AccessibleState.SELECTED );
   } else {
    accessibleContext.firePropertyChange( AccessibleContext.ACCESSIBLE_STATE_PROPERTY, AccessibleState.SELECTED, null );
   }
  }
  if( b != model.isSelected() ) {
   getModel().setSelected( b );
  }
 }
 //C Returns true if the menu's popup window is visible.
 //C @return true if the menu is visible, else false
 //Rem @Override
 public boolean isPopupMenuVisible() {
  ensurePopupMenuCreated();

  return popupMenu.isVisible();
 }
 //C Sets the visibility of the menu's popup.  If the menu is
 //C not enabled, this method will have no effect.
 //C @param b  a boolean value -- true to make the menu visible,
 //C           false to hide it
 //C @beaninfo
 //C      description: The popup menu's visibility
 //C           expert: true
 //C           hidden: true
 //Rem @Override
 public void setPopupMenuVisible( boolean b ) {
  if( ! isEnabled() ) {
   return;
  }

  boolean isVisible = isPopupMenuVisible();

  if( b != isVisible ) {
   ensurePopupMenuCreated();

   // Set location of popupMenu (pulldown or pullright)
   //  Perhaps this should be dictated by L&F
   if( ( b == true ) && isShowing() ) {
    Point p = getPopupMenuOrigin();
    getPopupMenu().show( this, p.x, p.y );
   } else {
    getPopupMenu().setVisible( false );
   }
  }
 }
 //C Computes the origin for the <code>AliceMenu</code>'s popup menu.
 //C @return a <code>Point</code> in the coordinate space of the
 //C        menu which should be used as the origin
 //C         of the <code>AliceMenu</code>'s popup menu
 //Rem @Override
 protected Point getPopupMenuOrigin() {
  int x = 0;
  int y = 0;
  JPopupMenu pm = getPopupMenu();

  // Figure out the sizes needed to caclulate the menu position
  Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
  screenSize.height -= 28; //HACK to avoid standard Windows Task Bar -JFP
  Dimension s = getSize();
  Dimension pmSize = pm.getSize();

  // For the first time the menu is popped up,
  // the size has not yet been initiated
  if( pmSize.width == 0 ) {
   pmSize = pm.getPreferredSize();
  }

  Point position = getLocationOnScreen();
  Container parent = getParent();

  if( parent instanceof JPopupMenu ) {

   // We are a submenu (pull-right)
   if( this.getComponentOrientation().isLeftToRight() ) {

    // First determine x:
    if( ( position.x + s.width + pmSize.width ) < screenSize.width ) {
     x = s.width;  // Prefer placement to the right
    } else {
     x = 0 - pmSize.width;  // Otherwise place to the left
    }
   } else {

    // First determine x:
    if( position.x < pmSize.width ) {
     x = s.width;  // Prefer placement to the right
    } else {
     x = 0 - pmSize.width;  // Otherwise place to the left
    }
   }
   // Then the y:
   if( ( position.y + pmSize.height ) < screenSize.height ) {
    y = 0;  // Prefer dropping down
   } else {
    y = s.height - pmSize.height;  // Otherwise drop 'up'
   }
  } else {

   // We are a toplevel menu (pull-down)
   if( this.getComponentOrientation().isLeftToRight() ) {

    // First determine the x:
    if( ( position.x + pmSize.width ) < screenSize.width ) {
     x = 0;  // Prefer extending to right
    } else {
     x = s.width - pmSize.width;  // Otherwise extend to left
    }
   } else {

    // First determine the x:
    if( ( position.x + s.width ) < pmSize.width ) {
     x = 0;  // Prefer extending to right
    } else {
     x = s.width - pmSize.width;  // Otherwise extend to left
    }
   }
   // Then the y:
   if( ( position.y + s.height + pmSize.height ) < screenSize.height ) {
    y = s.height;  // Prefer dropping down
   } else {
    y = 0 - pmSize.height;  // Otherwise drop 'up'
   }
  }

  return new Point( x, y );
 }
 //C Returns the suggested delay, in milliseconds, before submenus
 //C are popped up or down.
 //C Each look and feel (L&F) may determine its own policy for
 //C observing the <code>delay</code> property.
 //C In most cases, the delay is not observed for top level menus
 //C or while dragging.  The default for <code>delay</code> is 0.
 //C This method is a property of the look and feel code and is used
 //C to manage the idiosyncracies of the various UI implementations.
 //C @return the <code>delay</code> property
 //Rem @Override
 public int getDelay() {
  return delay;
 }
 //C Sets the suggested delay before the menu's <code>PopupMenu</code>
 //C is popped up or down.  Each look and feel (L&F) may determine
 //C it's own policy for observing the delay property.  In most cases,
 //C the delay is not observed for top level menus or while dragging.
 //C This method is a property of the look and feel code and is used
 //C to manage the idiosyncracies of the various UI implementations.
 //C @param       d the number of milliseconds to delay
 //C @exception   IllegalArgumentException if <code>d</code>
 //C                       is less than 0
 //C @beaninfo
 //C      description: The delay between menu selection and making the popup menu visible
 //C           expert: true
 //Rem @Override
 public void setDelay( int d ) {
  if( d < 0 ) {
   throw new IllegalArgumentException( "Delay must be a positive integer" );
  }

  delay = d;
 }

 private void ensurePopupMenuCreated() {
  if( popupMenu == null ) {
   this.popupMenu = new AlicePopupMenu();
   popupMenu.setInvoker( this );
   popupListener = createWinListener( popupMenu );
   popupMenu.addPopupMenuListener( new PopupMenuListener() {
    public void popupMenuWillBecomeVisible( PopupMenuEvent e ) {}


    public void popupMenuWillBecomeInvisible( PopupMenuEvent e ) {}


    public void popupMenuCanceled( PopupMenuEvent e ) {
     fireMenuCanceled();
    }
   } );
  }
 }
 //C Sets the location of the popup component.
 //C @param x the x coordinate of the popup's new position
 //C @param y the y coordinate of the popup's new position
 //Rem @Override
 public void setMenuLocation( int x, int y ) {
  if( popupMenu != null ) {
   popupMenu.setLocation( x, y );
  }
 }
 //C Appends a menu item to the end of this menu.
 //C Returns the menu item added.
 //C @param menuItem the <code>JMenuitem</code> to be added
 //C @return the <code>JMenuItem</code> added
 //Rem @Override
 public JMenuItem add( JMenuItem menuItem ) {
  AccessibleContext ac = menuItem.getAccessibleContext();
  ac.setAccessibleParent( this );
  ensurePopupMenuCreated();

  return popupMenu.add( menuItem );
 }
 //C Appends a component to the end of this menu.
 //C Returns the component added.
 //C @param c the <code>Component</code> to add
 //C @return the <code>Component</code> added
 //Rem @Override
 public Component add( Component c ) {
  if( c instanceof JComponent ) {
   AccessibleContext ac = ( (JComponent)c ).getAccessibleContext();

   if( ac != null ) {
    ac.setAccessibleParent( this );
   }
  }

  ensurePopupMenuCreated();
  popupMenu.add( c );

  return c;
 }
 //C Adds the specified component to this container at the given
 //C position. If <code>index</code> equals -1, the component will
 //C be appended to the end.
 //C @param     c   the <code>Component</code> to add
 //C @param     index    the position at which to insert the component
 //C @return    the <code>Component</code> added
 //C @see      #remove
 //C @see java.awt.Container#add(Component, int)
 //Rem @Override
 public Component add( Component c, int index ) {
  if( c instanceof JComponent ) {
   AccessibleContext ac = ( (JComponent)c ).getAccessibleContext();

   if( ac != null ) {
    ac.setAccessibleParent( this );
   }
  }

  ensurePopupMenuCreated();
  popupMenu.add( c, index );

  return c;
 }
 //C Creates a new menu item with the specified text and appends
 //C it to the end of this menu.
 //C @param s the string for the menu item to be added
 //Rem @Override
 public JMenuItem add( String s ) {
  return add( new JMenuItem( s ) );
 }
 //C Creates a new menu item attached to the specified
 //C <code>Action</code> object and appends it to the end of this menu.
 //C As of JDK 1.3, this is no longer the preferred method for adding
 //C <code>Actions</code> to
 //C a container. Instead it is recommended to configure a control with
 //C an action using <code>setAction</code>,
 //C and then add that control directly
 //C to the <code>Container</code>.
 //C @param a the <code>Action</code> for the menu item to be added
 //C @see Action
 //Rem @Override
 public JMenuItem add( Action a ) {
  JMenuItem mi = createActionComponent( a );
  mi.setAction( a );
  add( mi );

  return mi;
 }
 //C Factory method which creates the <code>JMenuItem</code> for
 //C <code>Action</code>s added to the <code>AliceMenu</code>.
 //C As of JDK 1.3, this is no
 //C longer the preferred method. Instead it is recommended to configure
 //C a control with an action using <code>setAction</code>,
 //C and then adding that
 //C control directly to the <code>Container</code>.
 //C @param a the <code>Action</code> for the menu item to be added
 //C @return the new menu item
 //C @see Action
 //Rem @Override
 protected JMenuItem createActionComponent( Action a ) {
  JMenuItem mi = new JMenuItem( (String)a.getValue( Action.NAME ), (Icon)a.getValue( Action.SMALL_ICON ) ) {
   //Rem @Override
   protected PropertyChangeListener createActionPropertyChangeListener( Action a ) {
    PropertyChangeListener pcl = createActionChangeListener( this );

    if( pcl == null ) {
     pcl = super.createActionPropertyChangeListener( a );
    }

    return pcl;
   }
  };

  mi.setHorizontalTextPosition( SwingConstants.RIGHT );
  mi.setVerticalTextPosition( SwingConstants.CENTER );
  mi.setEnabled( a.isEnabled() );

  return mi;
 }
 //C Returns a properly configured <code>PropertyChangeListener</code>
 //C which updates the control as changes to the <code>Action</code> occur.
 //C As of JDK 1.3, this is no longer the preferred method for adding
 //C <code>Action</code>s to a <code>Container</code>.
 //C Instead it is recommended to configure a control with
 //C an action using <code>setAction</code>, and then add that
 //C control directly
 //C to the <code>Container</code>.
 //Rem @Override
 protected PropertyChangeListener createActionChangeListener( JMenuItem b ) {
  return new ActionChangedListener( b );
 }

 private class ActionChangedListener implements PropertyChangeListener {
  JMenuItem menuItem;

  ActionChangedListener( JMenuItem mi ) {
   super();
   setTarget( mi );
  }

  public void propertyChange( PropertyChangeEvent e ) {
   String propertyName = e.getPropertyName();

   if( e.getPropertyName().equals( Action.NAME ) ) {
    String text = (String)e.getNewValue();
    menuItem.setText( text );
   } else if( propertyName.equals( "enabled" ) ) {
    Boolean enabledState = (Boolean)e.getNewValue();
    menuItem.setEnabled( enabledState.booleanValue() );
   } else if( e.getPropertyName().equals( Action.SMALL_ICON ) ) {
    Icon icon = (Icon)e.getNewValue();
    menuItem.setIcon( icon );
    menuItem.invalidate();
    menuItem.repaint();
   }
  }

  public void setTarget( JMenuItem b ) {
   this.menuItem = b;
  }
 }

 //C Appends a new separator to the end of the menu.
 //Rem @Override
 public void addSeparator() {
  ensurePopupMenuCreated();
  popupMenu.addSeparator();
 }
 //C Inserts a new menu item with the specified text at a
 //C given position.
 //C @param s the text for the menu item to add
 //C @param pos an integer specifying the position at which to add the
 //C               new menu item
 //C @exception IllegalArgumentException when the value of
 //C            <code>pos</code> < 0
 //Rem @Override
 public void insert( String s, int pos ) {
  if( pos < 0 ) {
   throw new IllegalArgumentException( "index less than zero." );
  }

  ensurePopupMenuCreated();
  popupMenu.insert( new JMenuItem( s ), pos );
 }
 //C Inserts the specified <code>JMenuitem</code> at a given position.
 //C @param mi the <code>JMenuitem</code> to add
 //C @param pos an integer specifying the position at which to add the
 //C               new <code>JMenuitem</code>
 //C @return the new menu item
 //C @exception IllegalArgumentException if the value of
 //C            <code>pos</code> < 0
 //Rem @Override
 public JMenuItem insert( JMenuItem mi, int pos ) {
  if( pos < 0 ) {
   throw new IllegalArgumentException( "index less than zero." );
  }

  AccessibleContext ac = mi.getAccessibleContext();
  ac.setAccessibleParent( this );
  ensurePopupMenuCreated();
  popupMenu.insert( mi, pos );

  return mi;
 }
 //C Inserts a new menu item attached to the specified <code>Action</code>
 //C object at a given position.
 //C @param a the <code>Action</code> object for the menu item to add
 //C @param pos an integer specifying the position at which to add the
 //C               new menu item
 //C @exception IllegalArgumentException if the value of
 //C            <code>pos</code> < 0
 //Rem @Override
 public JMenuItem insert( Action a, int pos ) {
  if( pos < 0 ) {
   throw new IllegalArgumentException( "index less than zero." );
  }

  ensurePopupMenuCreated();

  JMenuItem mi = new JMenuItem( (String)a.getValue( Action.NAME ), (Icon)a.getValue( Action.SMALL_ICON ) );
  mi.setHorizontalTextPosition( SwingConstants.RIGHT );
  mi.setVerticalTextPosition( SwingConstants.CENTER );
  mi.setEnabled( a.isEnabled() );
  mi.setAction( a );
  popupMenu.insert( mi, pos );

  return mi;
 }
 //C Inserts a separator at the specified position.
 //C @param       index an integer specifying the position at which to
 //C                    insert the menu separator
 //C @exception   IllegalArgumentException if the value of
 //C                       <code>index</code> < 0
 //Rem @Override
 public void insertSeparator( int index ) {
  if( index < 0 ) {
   throw new IllegalArgumentException( "index less than zero." );
  }

  ensurePopupMenuCreated();
  popupMenu.insert( new JPopupMenu.Separator(), index );
 }
 //C Returns the <code>JMenuItem</code> at the specified position.
 //C If the component at <code>pos</code> is not a menu item,
 //C <code>null</code> is returned.
 //C This method is included for AWT compatibility.
 //C @param pos    an integer specifying the position
 //C @exception   IllegalArgumentException if the value of
 //C                       <code>pos</code> < 0
 //C @return  the menu item at the specified position; or <code>null</code>
 //C        if the item as the specified position is not a menu item
 //Rem @Override
 public JMenuItem getItem( int pos ) {
  if( pos < 0 ) {
   throw new IllegalArgumentException( "index less than zero." );
  }

  Component c = getMenuComponent( pos );

  if( c instanceof JMenuItem ) {
   JMenuItem mi = (JMenuItem)c;

   return mi;
  }

  // 4173633
  return null;
 }
 //C Returns the number of items on the menu, including separators.
 //C This method is included for AWT compatibility.
 //C @return an integer equal to the number of items on the menu
 //C @see #getMenuComponentCount
 //Rem @Override
 public int getItemCount() {
  return getMenuComponentCount();
 }
 //C Returns true if the menu can be torn off.  This method is not
 //C yet implemented.
 //C @return true if the menu can be torn off, else false
 //C @exception  Error  if invoked -- this method is not yet implemented
 //Rem @Override
 public boolean isTearOff() {
  throw new Error( "boolean isTearOff() {} not yet implemented" );
 }
 //C Removes the specified menu item from this menu.  If there is no
 //C popup menu, this method will have no effect.
 //C @param    item the <code>JMenuItem</code> to be removed from the menu
 //Rem @Override
 public void remove( JMenuItem item ) {
  if( popupMenu != null ) {
   popupMenu.remove( item );
  }
 }
 //C Removes the menu item at the specified index from this menu.
 //C @param       pos the position of the item to be removed
 //C @exception   IllegalArgumentException if the value of
 //C                       <code>pos</code> < 0, or if <code>pos</code>
 //C                 is greater than the number of menu items
 //Rem @Override
 public void remove( int pos ) {
  if( pos < 0 ) {
   throw new IllegalArgumentException( "index less than zero." );
  }
  if( pos > getItemCount() ) {
   throw new IllegalArgumentException( "index greater than the number of items." );
  }
  if( popupMenu != null ) {
   popupMenu.remove( pos );
  }
 }
 //C Removes the component <code>c</code> from this menu.
 //C @param       c the component to be removed
 //Rem @Override
 public void remove( Component c ) {
  if( popupMenu != null ) {
   popupMenu.remove( c );
  }
 }
 //C Removes all menu items from this menu.
 //Rem @Override
 public void removeAll() {
  if( popupMenu != null ) {
   popupMenu.removeAll();
  }
 }
 //C Returns the number of components on the menu.
 //C @return an integer containing the number of components on the menu
 //Rem @Override
 public int getMenuComponentCount() {
  int componentCount = 0;

  if( popupMenu != null ) {
   componentCount = popupMenu.getComponentCount();
  }

  return componentCount;
 }
 //C Returns the component at position <code>n</code>.
 //C @param n the position of the component to be returned
 //C @return the component requested, or <code>null</code>
 //C            if there is no popup menu
 //Rem @Override
 public Component getMenuComponent( int n ) {
  if( popupMenu != null ) {
   return popupMenu.getComponent( n );
  }

  return null;
 }
 //C Returns an array of <code>Component</code>s of the menu's
 //C subcomponents.  Note that this returns all <code>Component</code>s
 //C in the popup menu, including separators.
 //C @return an array of <code>Component</code>s or an empty array
 //C        if there is no popup menu
 //Rem @Override
 public Component[] getMenuComponents() {
  if( popupMenu != null ) {
   return popupMenu.getComponents();
  }

  return new Component[0];
 }
 //C Returns true if the menu is a 'top-level menu', that is, if it is
 //C the direct child of a menubar.
 //C @return true if the menu is activated from the menu bar;
 //C         false if the menu is activated from a menu item
 //C         on another menu
 //Rem @Override
 public boolean isTopLevelMenu() {
  if( getParent() instanceof JMenuBar ) {
   return true;
  }

  return false;
 }
 //C Returns true if the specified component exists in the
 //C submenu hierarchy.
 //C @param c the <code>Component</code> to be tested
 //C @return true if the <code>Component</code> exists, false otherwise
 //Rem @Override
 public boolean isMenuComponent( Component c ) {

  // Are we in the MenuItem part of the menu
  if( c == this ) {
   return true;
  }
  // Are we in the PopupMenu?
  if( c instanceof JPopupMenu ) {
   JPopupMenu comp = (JPopupMenu)c;

   if( comp == this.getPopupMenu() ) {
    return true;
   }
  }

  // Are we in a Component on the PopupMenu
  int ncomponents = this.getMenuComponentCount();
  Component[] component = this.getMenuComponents();

  for( int i = 0; i < ncomponents; i++ ) {
   Component comp = component[i];

   // Are we in the current component?
   if( comp == c ) {
    return true;
   }
   // Hmmm, what about Non-menu containers?
   // Recursive call for the Menu case
   if( comp instanceof AliceMenu ) {
    AliceMenu subMenu = (AliceMenu)comp;

    if( subMenu.isMenuComponent( c ) ) {
     return true;
    }
   }
  }

  return false;
 }
 //C Returns the popupmenu associated with this menu.  If there is
 //C no popupmenu, it will create one.
 //Rem @Override
 public JPopupMenu getPopupMenu() {
  ensurePopupMenuCreated();

  return popupMenu;
 }
 //C Adds a listener for menu events.
 //C @param l the listener to be added
 //Rem @Override
 public void addMenuListener( MenuListener l ) {
  listenerList.add( MenuListener.class, l );
 }
 //C Removes a listener for menu events.
 //C @param l the listener to be removed
 //Rem @Override
 public void removeMenuListener( MenuListener l ) {
  listenerList.remove( MenuListener.class, l );
 }
 //C Notifies all listeners that have registered interest for
 //C notification on this event type.  The event instance
 //C is lazily created using the parameters passed into
 //C the fire method.
 //C @exception Error  if there is a <code>null</code> listener
 //C @see EventListenerList
 //Rem @Override
 protected void fireMenuSelected() {
  // Guaranteed to return a non-null array
  Object[] listeners = listenerList.getListenerList();

  // Process the listeners last to first, notifying
  // those that are interested in this event
  for( int i = listeners.length - 2; i >= 0; i -= 2 ) {
   if( listeners[i] == MenuListener.class ) {
    if( listeners[i + 1] == null ) {
     throw new Error( getText() + " has a NULL Listener!! " + i );
    } else {

     // Lazily create the event:
     if( menuEvent == null ) {
      menuEvent = new MenuEvent( this );
     }

     ( (MenuListener)listeners[i + 1] ).menuSelected( menuEvent );
    }
   }
  }
 }
 //C Notifies all listeners that have registered interest for
 //C notification on this event type.  The event instance
 //C is lazily created using the parameters passed into
 //C the fire method.
 //C @exception Error if there is a <code>null</code> listener
 //C @see EventListenerList
 //Rem @Override
 protected void fireMenuDeselected() {
  // Guaranteed to return a non-null array
  Object[] listeners = listenerList.getListenerList();

  // Process the listeners last to first, notifying
  // those that are interested in this event
  for( int i = listeners.length - 2; i >= 0; i -= 2 ) {
   if( listeners[i] == MenuListener.class ) {
    if( listeners[i + 1] == null ) {
     throw new Error( getText() + " has a NULL Listener!! " + i );
    } else {

     // Lazily create the event:
     if( menuEvent == null ) {
      menuEvent = new MenuEvent( this );
     }

     ( (MenuListener)listeners[i + 1] ).menuDeselected( menuEvent );
    }
   }
  }
 }
 //C Notifies all listeners that have registered interest for
 //C notification on this event type.  The event instance
 //C is lazily created using the parameters passed into
 //C the fire method.
 //C @exception Error if there is a <code>null</code> listener
 //C @see EventListenerList
 //Rem @Override
 protected void fireMenuCanceled() {
  // Guaranteed to return a non-null array
  Object[] listeners = listenerList.getListenerList();

  // Process the listeners last to first, notifying
  // those that are interested in this event
  for( int i = listeners.length - 2; i >= 0; i -= 2 ) {
   if( listeners[i] == MenuListener.class ) {
    if( listeners[i + 1] == null ) {
     throw new Error( getText() + " has a NULL Listener!! " + i );
    } else {

     // Lazily create the event:
     if( menuEvent == null ) {
      menuEvent = new MenuEvent( this );
     }

     ( (MenuListener)listeners[i + 1] ).menuCanceled( menuEvent );
    }
   }
  }
 }

 class MenuChangeListener implements ChangeListener, Serializable {
  boolean isSelected = false;

  public void stateChanged( ChangeEvent e ) {
   ButtonModel model = (ButtonModel)e.getSource();
   boolean modelSelected = model.isSelected();

   if( modelSelected != isSelected ) {
    if( modelSelected == true ) {
     fireMenuSelected();
    } else {
     fireMenuDeselected();
    }

    isSelected = modelSelected;
   }
  }
 }


 private ChangeListener createMenuChangeListener() {
  return new MenuChangeListener();
 }
 //C Creates a window-closing listener for the popup.
 //C @param p the <code>JPopupMenu</code>
 //C @return the new window-closing listener
 //C @see WinListener
 //Rem @Override
 protected WinListener createWinListener( JPopupMenu p ) {
  return new WinListener( p );
 }

 //C Messaged when the menubar selection changes to activate or
 //C deactivate this menu.
 //C Overrides <code>JMenuItem.menuSelectionChanged</code>.
 //C @param isIncluded  true if this menu is active, false if
 //C        it is not
 //Rem @Override
 public void menuSelectionChanged( boolean isIncluded ) {

  setSelected( isIncluded );
 }
 //C Returns an array of <code>MenuElement</code>s containing the submenu
 //C for this menu component.  If popup menu is <code>null</code> returns
 //C an empty array.  This method is required to conform to the
 //C <code>MenuElement</code> interface.  Note that since
 //C <code>JSeparator</code>s do not conform to the <code>MenuElement</code>
 //C interface, this array will only contain <code>JMenuItem</code>s.
 //C @return an array of <code>MenuElement</code> objects
 //Rem @Override
 public MenuElement[] getSubElements() {
  if( popupMenu == null ) {
   return new MenuElement[0];
  } else {
   MenuElement[] result = new MenuElement[1];
   result[0] = popupMenu;

   return result;
  }
 }
 // implements javax.swing.MenuElement
 //C Returns the <code>java.awt.Component</code> used to
 //C paint this <code>MenuElement</code>.
 //C The returned component is used to convert events and detect if
 //C an event is inside a menu component.
 //Rem @Override
 public Component getComponent() {
  return this;
 }
 //C <code>setAccelerator</code> is not defined for <code>AliceMenu</code>.
 //C Use <code>setMnemonic</code> instead.
 //C @param keyStroke  the keystroke combination which will invoke
 //C            the <code>JMenuItem</code>'s actionlisteners
 //C            without navigating the menu hierarchy
 //C @exception Error  if invoked -- this method is not defined for AliceMenu.
 //C            Use <code>setMnemonic</code> instead
 //C @beaninfo
 //C     description: The keystroke combination which will invoke the JMenuItem's
 //C                  actionlisteners without navigating the menu hierarchy
 //C          hidden: true
 //Rem @Override
 public void setAccelerator( KeyStroke keyStroke ) {
  throw new Error( "setAccelerator() is not defined for AliceMenu.  Use setMnemonic() instead." );
 }
 //C Processes any focus events, such as
 //C <code>FocusEvent.FOCUS_GAINED</code> or
 //C <code>FocusEvent.FOCUS_LOST</code>.
 //C @param e the <code>FocusEvent</code>
 //C @see FocusEvent
 //Rem @Override
 protected void processFocusEvent( FocusEvent e ) {
  switch( e.getID() ) {
  case FocusEvent.FOCUS_LOST:
   receivedKeyPressed = false;
   break;

  default:
   break;
  }

  super.processFocusEvent( e );
 }
 //C Processes key stroke events for this menu, such as mnemonics and
 //C accelerators.
 //C @param e  the key event to be processed
 //Rem @Override
 protected void processKeyEvent( KeyEvent e ) {

  boolean createMenuEvent = false;

  switch( e.getID() ) {
  case KeyEvent.KEY_PRESSED:
   if( isSelected() ) {
    createMenuEvent = receivedKeyPressed = true;
   } else {
    receivedKeyPressed = false;
   }

   break;

  case KeyEvent.KEY_RELEASED:
   if( receivedKeyPressed ) {
    receivedKeyPressed = false;
    createMenuEvent = true;
   }

   break;

  default:
   createMenuEvent = receivedKeyPressed;
  break;
  }
  if( createMenuEvent ) {
   MenuSelectionManager.defaultManager().processKeyEvent( e );
  }
  if( e.isConsumed() ) {
   return;
  }
  /* The "if" block below  fixes bug #4108907.
      Without this code, opened menus that
      weren't interested in TAB key events (most menus are not) would
      allow such events to propagate up until a component was found
      that was interested in the event. This would often result in
      the focus being moved to another component as a result of the
      TAB, while the menu stayed open. The behavior that is most
      probably desired is that menus are modal, and thus consume
      all keyboard events while they are open. This is implemented
      by the inner "if" clause. But if the desired behavior on TABs
      is that the menu should close and allow the focus to move,
      the "else" clause takes care of that. Note that this is probably
      not the right way to implement that behavior; instead, the menu
      should unpost whenever it looses focus, which would also fix
      another bug: 4156858.
      The fact that one has to special-case TABS here in AliceMenu code
      also offends me...
      hania 23 July 1998 */
  if( isSelected() && ( e.getKeyCode() == KeyEvent.VK_TAB || e.getKeyChar() == '\t' ) ) {
   if( (Boolean)UIManager.get( "Menu.consumesTabs" ) == Boolean.TRUE ) {
    e.consume();

    return;
   } else {
    MenuSelectionManager.defaultManager().clearSelectedPath();
   }
  }

  super.processKeyEvent( e );
 }
 //C Programatically performs a "click".  This overrides the method
 //C <code>AbstractButton.doClick</code> in order to make the menu pop up.
 //C @param pressTime  indicates the number of milliseconds the
 //C        button was pressed for
 //Rem @Override
 public void doClick( int pressTime ) {
  MenuElement[] me = buildMenuElementArray( this );
  MenuSelectionManager.defaultManager().setSelectedPath( me );
 }

 //C Build an array of menu elements - from <code>PopupMenu</code> to
 //C the root <code>JMenuBar</code>.
 //C @param  leaf  the leaf node from which to start building up the array
 //C @return the array of menu items
 private MenuElement[] buildMenuElementArray( AliceMenu leaf ) {
  Vector elements = new Vector();
  Component current = leaf.getPopupMenu();
  JPopupMenu pop;
  AliceMenu menu;
  JMenuBar bar;

  while( true ) {
   if( current instanceof JPopupMenu ) {
    pop = (JPopupMenu)current;
    elements.insertElementAt( pop, 0 );
    current = pop.getInvoker();
   } else if( current instanceof AliceMenu ) {
    menu = (AliceMenu)current;
    elements.insertElementAt( menu, 0 );
    current = menu.getParent();
   } else if( current instanceof JMenuBar ) {
    bar = (JMenuBar)current;
    elements.insertElementAt( bar, 0 );

    MenuElement[] me = new MenuElement[elements.size()];
    elements.copyInto( me );

    return me;
   }
  }
 }
 //C See <code>readObject</code> and <code>writeObject</code> in
 //C <code>JComponent</code> for more
 //C information about serialization in Swing.
 private void writeObject( ObjectOutputStream s )
 throws IOException {
  s.defaultWriteObject();

  if( ( ui != null ) && ( getUIClassID().equals( uiClassID ) ) ) {
   ui.installUI( this );
  }
 }
 //C Returns a string representation of this <code>AliceMenu</code>. This
 //C method is intended to be used only for debugging purposes, and the
 //C content and format of the returned string may vary between
 //C implementations. The returned string may be empty but may not
 //C be <code>null</code>.
 //C @return  a string representation of this AliceMenu.
 //Rem @Override
 protected String paramString() {
  return super.paramString();
 }
 /////////////////
 // Accessibility support
 ////////////////
 //C Gets the AccessibleContext associated with this AliceMenu.
 //C For JMenus, the AccessibleContext takes the form of an
 //C AccessibleJMenu.
 //C A new AccessibleJMenu instance is created if necessary.
 //C @return an AccessibleJMenu that serves as the
 //C         AccessibleContext of this AliceMenu
 //Rem @Override
 public AccessibleContext getAccessibleContext() {
  if( accessibleContext == null ) {
   accessibleContext = new AccessibleJMenu();
  }

  return accessibleContext;
 }
 //C This class implements accessibility support for the
 //C <code>AliceMenu</code> class.  It provides an implementation of the
 //C Java Accessibility API appropriate to menu user-interface elements.
 //C <p>
 //C <strong>Warning:</strong>
 //C Serialized objects of this class will not be compatible with
 //C future Swing releases.  The current serialization support is appropriate
 //C for short term storage or RMI between applications running the same
 //C version of Swing.  A future release of Swing will provide support for
 //C long term persistence.
 protected class AccessibleJMenu extends AccessibleAbstractButton implements ChangeListener, AccessibleSelection {
  public AccessibleJMenu() {
   super();
   AliceMenu.this.addChangeListener( this );
  }

  public void stateChanged( ChangeEvent e ) {
   firePropertyChange( AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY, new Boolean( false ), new Boolean( true ) );
  }

   //C Returns the number of accessible children in the object.  If all
  //C of the children of this object implement Accessible, than this
  //C method should return the number of children of this object.
   //C @return the number of accessible children in the object.
   //Rem @Override
  public int getAccessibleChildrenCount() {
   Component[] children = getMenuComponents();
   int count = 0;

   //LOB for (Component element : children)
   int t_tam = children!=null ? children.length : 0;
   for (int i_i=0; i_i<t_tam; i_i++) {
    if( children[i_i] instanceof Accessible ) { //L if( element instanceof Accessible )
     count++;
    }
   }

   return count;
  }
   //C Returns the nth Accessible child of the object.
   //C @param i zero-based index of child
  //C @return the nth Accessible child of the object
   //Rem @Override
  public Accessible getAccessibleChild( int i ) {
   Component[] children = getMenuComponents();
   int count = 0;

   //LOB for (Component element : children)
   int t_tam = children!=null ? children.length : 0;
   for (int i_i=0; i_i<t_tam; i_i++) {
    if( children[i_i] instanceof Accessible ) { //L if( element instanceof Accessible )
     if( count == i ) {
      if( children[i_i] instanceof JComponent ) { //L if( element instanceof JComponent )
       // FIXME:  [[[WDW - probably should set this when
       // the component is added to the menu.  I tried
       // to do this in most cases, but the separators
       // added by addSeparator are hard to get to.]]]
       AccessibleContext ac = ( (Accessible)children[i_i] ).getAccessibleContext(); //L ( (Accessible)element ).getAccessibleContext()
       ac.setAccessibleParent( AliceMenu.this );
      }

      return (Accessible)children[i_i]; //L return (Accessible)element;
     } else {
      count++;
     }
    }
   } // for (int i_i=0; i_i<t_tam; i_i++)

   return null;
  }
   //C Get the role of this object.
   //C @return an instance of AccessibleRole describing the role of the
  //C object
  //C @see AccessibleRole
   //Rem @Override
  public AccessibleRole getAccessibleRole() {
   return AccessibleRole.MENU;
  }
   //C Get the AccessibleSelection associated with this object.  In the
  //C implementation of the Java Accessibility API for this class,
  //C return this object, which is responsible for implementing the
  //C AccessibleSelection interface on behalf of itself.
   //C @return this object
   //Rem @Override
  public AccessibleSelection getAccessibleSelection() {
   return this;
  }
   //C Returns 1 if a sub-menu is currently selected in this menu.
   //C @return 1 if a menu is currently selected, else 0
   public int getAccessibleSelectionCount() {
   MenuElement[] me = MenuSelectionManager.defaultManager().getSelectedPath();

   if( me != null ) {
    for( int i = 0; i < me.length; i++ ) {
     if( me[i] == AliceMenu.this ) {  // this menu is selected
      if( ( i + 1 ) < me.length ) {
       return 1;
      }
     }
    }
   }

   return 0;
  }
   //C Returns the currently selected sub-menu if one is selected,
  //C otherwise null (there can only be one selection, and it can
  //C only be a sub-menu, as otherwise menu items don't remain
  //C selected).
   public Accessible getAccessibleSelection( int i ) {

   // if i is a sub-menu & popped, return it
   if( ( i < 0 ) || ( i >= getItemCount() ) ) {
    return null;
   }

   MenuElement[] me = MenuSelectionManager.defaultManager().getSelectedPath();

   if( me != null ) {
    for( int j = 0; j < me.length; j++ ) {
     if( me[j] == AliceMenu.this ) {  // this menu is selected

      // so find the next JMenuItem in the MenuElement
      // array, and return it!
      while( ( ++j ) < me.length ) {
       if( me[j] instanceof JMenuItem ) {
        return (Accessible)me[j];
       }
      }
     }
    }
   }

   return null;
  }
   //C Returns true if the current child of this object is selected
  //C (that is, if this child is a popped-up submenu).
   //C @param i the zero-based index of the child in this Accessible
  //C object.
  //C @see AccessibleContext#getAccessibleChild
   public boolean isAccessibleChildSelected( int i ) {

   // if i is a sub-menu and is pop-ed up, return true, else false
   MenuElement[] me = MenuSelectionManager.defaultManager().getSelectedPath();

   if( me != null ) {
    JMenuItem mi = AliceMenu.this.getItem( i );
    //LOB for (MenuElement element : me)
    int t_tam = me!=null ? me.length : 0;
    for (int i_i=0; i_i<t_tam; i_i++) {
     if( me[i_i] == mi ) { //L if( element == mi )
      return true;
     }
    }
   }

   return false;
  }
   //C Selects the <code>i</code>th menu in the menu.
  //C If that item is a submenu,
  //C it will pop up in response.  If a different item is already
  //C popped up, this will force it to close.  If this is a sub-menu
  //C that is already popped up (selected), this method has no
  //C effect.
   //C @param i the index of the item to be selected
  //C @see #getAccessibleStateSet
   public void addAccessibleSelection( int i ) {
   if( ( i < 0 ) || ( i >= getItemCount() ) ) {
    return;
   }

   JMenuItem mi = getItem( i );

   if( mi != null ) {
    if( mi instanceof AliceMenu ) {
     MenuElement[] me = buildMenuElementArray( (AliceMenu)mi );
     MenuSelectionManager.defaultManager().setSelectedPath( me );
    } else {
     mi.doClick();
     MenuSelectionManager.defaultManager().setSelectedPath( null );
    }
   }
  }
   //C Removes the nth item from the selection.  In general, menus
   //C can only have one item within them selected at a time
   //C (e.g. one sub-menu popped open).
   //C @param i the zero-based index of the selected item
   public void removeAccessibleSelection( int i ) {
   if( ( i < 0 ) || ( i >= getItemCount() ) ) {
    return;
   }

   JMenuItem mi = getItem( i );

   if( ( mi != null ) && mi instanceof AliceMenu ) {
    if( ( (AliceMenu)mi ).isSelected() ) {
     MenuElement[] old = MenuSelectionManager.defaultManager().getSelectedPath();
     MenuElement[] me = new MenuElement[old.length - 2];

     for( int j = 0; j < ( old.length - 2 ); j++ ) {
      me[j] = old[j];
     }

     MenuSelectionManager.defaultManager().setSelectedPath( me );
    }
   }
  }
   //C Clears the selection in the object, so that nothing in the
  //C object is selected.  This will close any open sub-menu.
   public void clearAccessibleSelection() {

   // if this menu is selected, reset selection to only go
   // to this menu; else do nothing
   MenuElement[] old = MenuSelectionManager.defaultManager().getSelectedPath();

   if( old != null ) {
    for( int j = 0; j < old.length; j++ ) {
     if( old[j] == AliceMenu.this ) {  // menu is in the selection!
      MenuElement[] me = new MenuElement[j + 1];
      System.arraycopy( old, 0, me, 0, j );
      me[j] = AliceMenu.this.getPopupMenu();
      MenuSelectionManager.defaultManager().setSelectedPath( me );
     }
    }
   }
  }
   //C Normally causes every selected item in the object to be selected
  //C if the object supports multiple selections.  This method
  //C makes no sense in a menu bar, and so does nothing.
   public void selectAllAccessibleSelection() {}

 }  // inner class AccessibleJMenu

}
